#include <string>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>

#include "Include/INetSdk.h"
#include "Include/IAlarmSdk.h"
#include "Include/AuxLibs/Json/json.h"
#include "Include/AuxLibs/Json/value.h"

#include "modbus-srv.h"
#include "gige.h"
#include "sensor.h"
#include "ctrl.h"
#include "algo_mgr.h"
#include "util.h"
#include "encode.h"
#include "sds.h"

typedef Json::FastWriter CConfigWriter;
typedef Json::Reader CConfigReader;

IAlarmSdk* alarm_sdk = NULL;     
ILocalNetSDK* local_sdk = NULL;

static int _get_table_enable_channel(Json::Value &table)
{
    int enable = 0;
    for (int i=0; i<2; i++) {
        if (GetCgiJSValue_Int(table["AlarmCfg"][i],"Enable",enable) != 0){
            __log_mesg("Parse error.\n");
            return -1;
        }
        if (enable) {
            return i;
        }
    }
    return -1;
}

static int _get_file_enable_channel(char* filename) 
{
    char file[256];
    sprintf(file, "%s/%s", MTPath, filename);
    char* data = __read_file(file);
    if (data == NULL) return -1;
    Json::Value table;
    CConfigReader JsonReader;
    if (JsonReader.parse(data, table) == false) {
        __log_mesg("parse file=>%s error.\n", file);
        free(data);
        return -1;
    }
    free(data);
    return _get_table_enable_channel(table);
}

static int _write_file(const char* filename, const Json::Value &table) 
{
    std::string stream;
    CConfigWriter JsonWtire(stream);
    JsonWtire.write(table);
    char fullname[1024];
    sprintf(fullname, "%s/%s", MTPath, filename);
    printf("Write %s\n", fullname);
    return __write_file(fullname, stream.c_str(), stream.length());
}

char* _get_device_ip()
{
    static char ip[32] = {0};
    int sock_get_ip;  
    struct sockaddr_in *sin;  
    struct ifreq ifr_ip;     
  
    if ((sock_get_ip=socket(AF_INET, SOCK_STREAM, 0)) == -1)  {  
         return NULL;  
    }   
    memset(&ifr_ip, 0, sizeof(ifr_ip));     
    strncpy(ifr_ip.ifr_name, "eth0", sizeof(ifr_ip.ifr_name) - 1);     
    if(ioctl(sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0) {     
         return NULL;     
    }       
    sin = (struct sockaddr_in *)&ifr_ip.ifr_addr;     
    strcpy(ip,inet_ntoa(sin->sin_addr));          
    close(sock_get_ip);
    return ip;  
}

/**
 * @brief 外部报警接口。
 * 济宁云项目，相机外部报警接对射传感器，程序通过此接口，接受传感器信号
 * 
 * @param channel 
 * @param type 
 * @param state 
 * @param userdata 
 */
void on_alarm(int channel, unsigned int type, int state,unsigned long userdata)
{
    //__log_mesg("CNetSdkApp::OnAlarm():channel = %d type = %d state = %d\n",channel,type,state);
    if (type == VF_ALARM_LOCAL) {
        __log_mesg("Signal-val. state = %d.\n" ,state);
        if (state == 1) {
            sleep(get_beam_delay_time());
            set_beam_signal();
        }
    }
}

bool _check_test_code(std::string& code)
{
    return (0 == code.compare("appEventStrickleTest"))      //刮板机测试
        || (0 == code.compare("appEventAntiBlockingTest"))  //堵物测试
        || (0 == code.compare("appEventRoughTest"))         //合介桶跑粗检测
        || (0 == code.compare("appEventLiquidLevelTest"))   //液位测试
        || (0 == code.compare("appEventVolumeTest"))        //体积测试
        || (0 == code.compare("appEventBeltTest"))          //皮带测试
        || (0 == code.compare("appEventSlimeTest"));        //煤泥桶跑粗检测
}

void _proc_message(std::string &code, const Json::Value &table)
{
    int chn = -1;
    Json::Value ctable = table; // const变量用于non-const形数引起问题。。。
    chn = _get_table_enable_channel(ctable);
    algo_info* algo = get_task_algo();
    if (algo == NULL) { // 当前没有运行算法
        if (chn != -1) {
            _write_file(code.c_str(), table);
            start_task(code.c_str(), chn, NULL, 0);
        }
    } else { // 当前有算法在运行
        if (!strcmp(algo->name, code.c_str())) {
            stop_task(); // 由于区分不出是否修改了不同通道的配置，统一停止算法
            _write_file(code.c_str(), table);
            if (chn != -1) {
                start_task(code.c_str(), chn, NULL, 0);
            }
        }
    }
}

// 硬件电路板测试。
int _test_board(std::string& code)
{
    // 点击防堵页面测试按钮，测试和外接板485通信
    if (0 == code.compare("appEventAntiBlockingTest")) {
        ctrl_test();
        return 1;
    } else if (0 == code.compare("appEventRoughTest")) {
        ctrl_relay_on(0);
        return 1;
    } else if (0 == code.compare("appEventLiquidLevelTest")) {
        ctrl_relay_off(0);
        return 1;
    } else if (0 == code.compare("appEventVolumeTest")) {
        ctrl_relay_on(1);
        return 1;
    } else if (0 == code.compare("appEventBeltTest")) {
        ctrl_relay_off(1);
        return 1;
    }
    return 0;
}

void _replace(std::string& new_code, std::string& old_code)
{
    if (0 == old_code.compare("appEventSlime")) {
        new_code = "appEventSpray";
    } else if (0 == old_code.compare("appEventMTRough")) {
        new_code = "appEventTarpa";
    } else if (0 == old_code.compare("appEventLiquidLevel")) {
        new_code = "appEventDebris";
    } else {
        new_code = old_code;
    }
}

/**
 * @brief web和程序交互接口回调
 * 
 * @param code 算法名或者测试名
 * @param index 通道号 
 * @param action 
 * @param table 数据 
 */
void on_message(std::string code, int index , appEventAction action, const Json::Value &table)
{
    __log_mesg("code=[%s],index=[%d],action=[%d]\n", code.c_str(), index, action);

    if (_test_board(code)) return;

    // 点击测试按钮
    if (_check_test_code(code))
    {
        __log_mesg("debug_task_once.\n");;
        debug_task_once();
        return;
    }

    //FIXME 
    //appEventSpray替换appEventSlime
    //appEventTarpa替换appEventMTRough
    std::string code_;
    _replace(code_, code);

    _proc_message(code_, table);
}

void _init()
{
    if (modbus_start(NULL, MODBUS_DEFAULT_PORT)) {
        __log_mesg("modbus-srv start failed.\n");
        exit(1);
    }

    // 创建编码通道   
    encode_create(13, 1280, 720);

    // 设置alarm_sdk
    AlarmSDKInit();
	alarm_sdk = IAlarmSdk::CreatNew();
    if (alarm_sdk == 0) {
        exit(1);
    }
    if (alarm_sdk->start("mtapp", _get_device_ip(), 1883)) {
        exit(1);
    }
    alarm_sdk->attach(on_message);

    // 设置local_sdk
    LocalNetSDKInit();
    local_sdk = ILocalNetSDK::CreatNew();
    local_sdk->SetServerInfo((char *)"127.0.0.1",80);
    local_sdk->SetNamepasswd("admin","741258");
    local_sdk->AttachDelegateAlarm(on_alarm, (unsigned long)local_sdk);
    if (local_sdk->Login()) {
        __log_mesg("Login device failed.\n");
        exit(1);
    }

    // 和控制板串口通信
    if (ctrl_module_init()) {
        __log_mesg("ctrl_module_init() failed.\n");
        exit(1);
    }

    // 如果配置了工业相机，就一直尝试连接，直至连接成功
    if(get_gige_connected()) {
        do {
            if (!gige_open()) { 
                break;
            }
            __log_mesg("Gige camera open failed. retry.\n");
            sleep(1);
        } while (1);
        set_all_gige_propertise();
    }
    

    if (sensor_open()) {
        __log_mesg("Device open failed.\n");
        exit(1);
    }

}

void _try_start_algo(int* mock_seq, int mock_len)
{
    int chn = -1;
    for (size_t i=0; i<sizeof(algotable)/sizeof(algo_info); i++) {
        chn = _get_file_enable_channel(algotable[i].name) ;
        if (chn != -1) {
            start_task(algotable[i].name, chn, mock_seq, mock_len);
            __log_mesg("Start %s at channel %d.\n", algotable[i].name, chn);
            return;
        }
    }
    __log_mesg("No algo started.\n");
}

static int* _parse_argv(char* argv, int* length);

static unsigned long cronloops = 0;
#define run_with_period(_sec_) if ((cronloops != 0) || !(cronloops%_sec_==0))
void _cron()
{
    run_with_period(60) {
        alarm_sdk->notify("heartbeat");
    }
    cronloops++;
}

int main(int argc, char** argv)
{
    int rv = system("/mnt/meiteng/m.sh");
    __log_mesg("script return value => %d\n",rv);
    sleep(20);
    
    _init();

    int* mock_seq = NULL;
    int mock_len = 0;
    if (argc == 2) mock_seq = _parse_argv(argv[1], &mock_len);
    
    _try_start_algo(mock_seq, mock_len);
    
    while(1) {
        sleep(1);
        _cron();
    } 
}

// 测试任务模块
int _test_task()
{
    while(1) {
        char cmd[256];
        printf("Enter cmd:\n");
        scanf("%s", cmd);

        if (!strcmp(cmd, "appEventStrickle")) {
            start_task(cmd, 1, NULL, 0);
        } else if (!strcmp(cmd, "stop")) {
            stop_task();
        } else if (!strcmp(cmd, "appEventVolume")) {
            start_task(cmd, 1, NULL, 0);
        } else if (!strcmp(cmd, "debug")) {
            debug_task_once();
        } else if (!strcmp(cmd, "quit")) {
            break;
        } else {
            printf("Unkown cmd.\n try again.\n");
        }
    }
    return 1;
}

/**
 * @brief 返回帧结果序列
 * 
 * @param argv 参数 eg:100-2-30-0-70
 * @param length 序列长度
 * @return int* 序列首地址，外部释放
 */
int* _parse_argv(char* argv, int* length) {
    int count;
    sds * splits = sdssplitlen(argv, strlen(argv), "-", 1, &count);
    if (count < 2) return NULL;
    *length = atoi(splits[0]);
    int* seq = (int*)malloc(sizeof(int) * (*length));
    int last_len = 0;
    for (int i=1; i<count; i=i+2) {
        int len = atoi(splits[i]);
        int value = atoi(splits[i+1]);
        for (int j=0; j<len; j++) {
            int index = j+last_len;
            seq[index] = value;
        }
        last_len += len;
    }
    sdsfreesplitres(splits, count);
    return seq;
}